package lumis.search;

import lumis.portal.PortalException;
import lumis.portal.UnexpectedException;
import lumis.util.XmlUtil;

import org.w3c.dom.Node;

/**
 * Context for search infrastructure, that gives access to search configuration. 
 * <p>The class must be {@link SearchContext#init(String) initialized} before being called.  
 *
 * @version $Revision: 6558 $ $Date: 2007-04-24 15:04:46 -0300 (Tue, 24 Apr 2007) $
 * @since 4.0.4
 */
public abstract class SearchContext
{
	private static boolean initialized = false;
	private static Node searchConfigNode = null;

	/**
	 * Initialize the search context. This method reads and cache the search configuration, present in the file <code>searchconfig.xml</code>.
	 * <p> This method must be callled once before calls to any other method can be made, or a {@link SearchException} will be thrown.  
	 * @param searchConfigPath path to the configuration file <code>searchconfig.xml</code>.
	 * @throws PortalException
	 * @since 4.0.4
	 */
	public synchronized static void init(String searchConfigPath) throws PortalException
	{
		try
		{
			if (initialized)
				return;
			SearchContext.searchConfigNode = XmlUtil.getDocumentFromFile(searchConfigPath).getDocumentElement();
			initialized = true;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	/**
	 * Checks if the context was {@link SearchContext#init(String) initialized}.
	 * @throws PortalException if the context was not {@link SearchContext#init(String) initialized}.
	 * @since 4.0.4
	 */
	private static void checkIfInitialized() throws SearchException
	{
		if(!initialized)
			throw new SearchException("STR_SEARCHCONTEXT_NOT_INITIALIZED");
	}

	/**
	 * Returns the configuration {@link Node node} for the a search engine. 
	 * <p>If the search engine was not specified (passing null as argument), the default search engine will be used (which is the first declared in the configuration file).
	 * @param searchEngineId search engine identifier, or null for the default search engine
	 * @return configuration {@link Node node} for the search engine
	 * @throws PortalException if the context was not initialized or the given search engine was not found in the configuration.
	 * @since 4.0.4
	 */
	public static Node getSearchEngineConfigNode(String searchEngineId) throws PortalException
	{
		try
		{
			checkIfInitialized();
			Node configNode = null;
			if (searchEngineId == null)
			{
				configNode = XmlUtil.selectSingleNode("searchEngines/searchEngine", SearchContext.searchConfigNode);
				if (configNode == null)
					throw new SearchException("NO_SEARCH_ENGINE_CONFIGURED");
			}
			else
			{
				configNode = XmlUtil.selectSingleNode("searchEngines/searchEngine[@id='" + searchEngineId + "']", SearchContext.searchConfigNode);
				if (configNode == null)
					throw new SearchException("SEARCH_ENGINE_NOT_FOUND;" + searchEngineId);
			}

			return configNode;
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	/**
	 * Returns the configuration {@link Node node} for one of the search engine's indexers. 
	 * <p>If the search engine was not specified (passing null as argument), the default search engine will be used (which is the first declared in the configuration file).
	 * <p>If the indexer was not specified (passing null as argument), the default indexer will be used (which is the first declared in the search engine configuration).
	 * @param searchEngineId search engine identifier, or null for the default search engine
	 * @param indexerId indexer identifier, or null for the default indexer of the search engine
	 * @return configuration {@link Node node} for the indexer
	 * @throws PortalException if the context was not initialized or the given search engine or the indexer was not found in the configuration.
	 * @since 4.0.4
	 */
	public static Node getIndexerConfigNode(String searchEngineId, String indexerId) throws PortalException
	{
		try
		{
			checkIfInitialized();
			Node searchEngineNode = getSearchEngineConfigNode(searchEngineId);
			Node indexerNode = null;
			if (indexerId == null)
			{
				indexerNode = XmlUtil.selectSingleNode("indexers/indexer", searchEngineNode);
				if (indexerNode == null)
					throw new SearchException("NO_INDEXER_CONFIGURED_IN_SEARCH_ENGINE;" + searchEngineId);
			}
			else
			{
				indexerNode = XmlUtil.selectSingleNode("indexers/indexer[@id='" + indexerId + "']", searchEngineNode);
				if (indexerNode == null)
					throw new SearchException("INDEXER_NOT_FOUND_IN_SEARCH_ENGINE;" + indexerId + ";" + searchEngineId);
			}

			return indexerNode;
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	/**
	 * Returns the configuration {@link Node node}s for all indexers of a search engine. 
	 * @param searchEngineId search engine identifier, or null for the default search engine
	 * @return configuration {@link Node node}s for the indexers
	 * @throws PortalException if the context was not initialized or the given search engine was not found in the configuration.
	 * @since 4.0.4
	 */
	public static Node[] getIndexerConfigNodes(String searchEngineId) throws PortalException
	{
		try
		{
			checkIfInitialized();
			Node searchEngineNode = getSearchEngineConfigNode(searchEngineId);
			return XmlUtil.selectNodes("indexers/indexer", searchEngineNode);
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	/**
	 * Returns the configuration {@link Node node} for one of the search engine's searchers. 
	 * <p>If the search engine was not specified (passing null as argument), the default search engine will be used (which is the first declared in the configuration file).
	 * <p>If the searcher was not specified (passing null as argument), the default searcher will be used (which is the first declared in the search engine configuration).
	 * @param searchEngineId search engine identifier, or null for the default search engine
	 * @param searcherId searcher identifier, or null for the default searcher of the search engine
	 * @return configuration {@link Node node} for the searcher
	 * @throws PortalException if the context was not initialized or the given search engine or the searcher was not found in the configuration.
	 * @since 4.0.4
	 */
	public static Node getSearcherConfigNode(String searchEngineId, String searcherId) throws PortalException
	{
		try
		{
			checkIfInitialized();
			Node searchEngineNode = getSearchEngineConfigNode(searchEngineId);
			Node searcherNode = null;
			if (searcherId == null)
			{
				searcherNode = XmlUtil.selectSingleNode("searchers/searcher", searchEngineNode);
				if (searcherNode == null)
					throw new SearchException("NO_SEARCHER_CONFIGURED_IN_SEARCH_ENGINE;" + searchEngineId);
			}
			else
			{
				searcherNode = XmlUtil.selectSingleNode("searchers/searcher[@id='" + searcherId + "']", searchEngineNode);
				if (searcherNode == null)
					throw new SearchException("SEARCHER_NOT_FOUND_IN_SEARCH_ENGINE;" + searcherId + ";" + searchEngineId);
			}

			return searcherNode;
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	/**
	 * Returns the configuration {@link Node node}s for all searchers of a search engine. 
	 * @param searchEngineId search engine identifier, or null for the default search engine
	 * @return configuration {@link Node node}s for the searchers
	 * @throws PortalException if the context was not initialized or the given search engine was not found in the configuration.
	 * @since 4.0.4
	 */
	public static Node[] getSearcherConfigNodes(String searchEngineId) throws PortalException
	{
		try
		{
			checkIfInitialized();
			Node searchEngineNode = getSearchEngineConfigNode(searchEngineId);
			return XmlUtil.selectNodes("searchers/searcher", searchEngineNode);
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	/**
	 * Returns the criteria {@link Node node} for a content criteria, which is used to check
	 * certain criteria against a {@link SearchContent} and eventually use a configuration defined
	 * specifically to this criteria.
	 * @param criteriaId criteria identification
	 * @return criteria {@link Node node}
	 * @throws PortalException
	 * @since 4.0.4
	 */
	public static Node getCriteriaNode(String criteriaId) throws PortalException
	{
		checkIfInitialized();
		Node criteriaNode = XmlUtil.selectSingleNode("criterias/criteria[@id='" + criteriaId + "']", searchConfigNode);
		if (criteriaNode == null)
			throw new PortalException("STR_CRITERIA_NODE_NOT_FOUND;" + criteriaId);
		return criteriaNode;
	}

}
